#ifndef __IM_BASE_TYPES_H__
#define __IM_BASE_TYPES_H__

#include <stddef.h>
#include <imbus_config.h>

/**
 * @defgroup IMBaseTypes Primitive Data Types
 * @ingroup PublicIME
 * @{
 */

IM_BEGIN_DECLS

/**
 * @typedef IMType
 * @brief A data type which holds an id of a specified data type
 */
typedef unsigned int                IMType;

/**
 * @brief A data type which holds the size of a specified data or
 * array, or an index.
 */
typedef size_t                      IMSize;

/**
 * @brief A data type which holds a single signed byte.
 */
typedef char                        IMChar;

/**
 * @brief A data type which holds a single unsigned byte.
 */
typedef unsigned char               IMUChar;

/**
 * @brief A data type which holds a single signed integer.
 */
typedef int                         IMInt;

/**
 * @brief A data type which holds a single unsigned integer.
 */
typedef unsigned int                IMUInt;

/**
 * @brief A data type which holds a single boolean value.
 *
 * an IMBool variable shall only take two different values:
 * TRUE or FALSE.
 */
typedef int                         IMBool;

/**
 * @brief A data type which holds a single double value.
 */
typedef double                      IMDouble;

/**
 * @brief A data type which holds a single pointer.
 *
 * An IMPointer variable can be pointed to any type of data, but
 * in most case, it may point to an IMObject instance.
 */
typedef void*                       IMPointer;

/**
 * @brief A data type which holds a single const pointer.
 */
typedef const void*                 IMConstPointer;

/**
 * @brief Function to compare two data to see whether they are equal or not.
 */
typedef IMBool                      (*IMEqualFunc)          (IMConstPointer     a,
                                                             IMConstPointer     b);
/**
 * @brief Function to compare two data.
 * @return -1 if a < b, 0 if a == b, 1 if a > b
 */
typedef IMInt                       (*IMCompareFunc)        (IMConstPointer     a,
                                                             IMConstPointer     b);
/**
 * @brief Function to compare two data by using a specified user_data.
 * @return -1 if a < b, 0 if a == b, 1 if a > b
 */
typedef IMInt                       (*IMCompareDataFunc)    (IMConstPointer     a,
                                                             IMConstPointer     b,
                                                             IMPointer          user_data);
/**
 * @brief Function to manipulate a specified data.
 */
typedef void                        (*IMFunc)               (IMPointer          data,
                                                             IMPointer          user_data);
/**
 * @brief Function to destroy a specified data.
 *
 * For example it can be im_object_unref() to release an specified object.
 */
typedef void                        (*IMDestroyFunc)        (IMPointer          data);

/* Define min and max constants for the fixed size numerical types */
#define IM_MININT16         ((IMInt16) 0x8000)
#define IM_MAXINT16         ((IMInt16) 0x7fff)
#define IM_MAXUINT16        ((IMInt16) 0xffff)

#define IM_MININT32         ((IMInt32) 0x80000000)
#define IM_MAXINT32         ((IMInt32) 0x7fffffff)
#define IM_MAXUINT32        ((IMInt32) 0xffffffff)

#define IM_MININT64         ((IMInt64) IM_IMINT64_CONSTANT(0x8000000000000000))
#define IM_MAXINT64         IM_IMINT64_CONSTANT(0x7fffffffffffffff)
#define IM_MAXUINT64        IM_IMINT64_CONSTANT(0xffffffffffffffffU)

/* Macros to convert between pointer and various int types. */
#define IM_POINTER_TO_INT(p)    ((IMInt) (p))
#define IM_POINTER_TO_UINT(p)   ((IMUInt) (p))
#define IM_INT_TO_POINTER(i)    ((IMPointer) (i))
#define IM_UINT_TO_POINTER(u)   ((IMPointer) (u))
#define IM_POINTER_TO_SIZE(p)   ((IMSize) (p))
#define IM_SIZE_TO_POINTER(s)   ((IMPointer) (s))

/* Portable endian checks and conversions
 *
 * imbus_config.h defines IM_BYTE_ORDER which expands to one of
 * the below macros.
 */
#define IM_LITTLE_ENDIAN 1234
#define IM_BIG_ENDIAN    4321

/* Basic bit swapping functions
 */
#define IMUINT16_SWAP_LE_BE_CONSTANT(val)       ((IMUInt16) (   \
        (IMUInt16) ((IMUInt16) (val) >> 8) |                    \
        (IMUInt16) ((IMUInt16) (val) << 8)))

#define IMUINT32_SWAP_LE_BE_CONSTANT(val)       ((IMUInt32) (   \
        (((IMUInt32) (val) & (IMUInt32) 0x000000ffU) << 24) |   \
        (((IMUInt32) (val) & (IMUInt32) 0x0000ff00U) <<  8) |   \
        (((IMUInt32) (val) & (IMUInt32) 0x00ff0000U) >>  8) |   \
        (((IMUInt32) (val) & (IMUInt32) 0xff000000U) >> 24)))

#define IMUINT64_SWAP_LE_BE_CONSTANT(val)       ((IMUInt64) (               \
        (((IMUInt64) (val) &                                                \
            (IMUInt64) IM_IMINT64_CONSTANT (0x00000000000000ffU)) << 56) |  \
        (((IMUInt64) (val) &                                                \
            (IMUInt64) IM_IMINT64_CONSTANT (0x000000000000ff00U)) << 40) |  \
        (((IMUInt64) (val) &                                                \
            (IMUInt64) IM_IMINT64_CONSTANT (0x0000000000ff0000U)) << 24) |  \
        (((IMUInt64) (val) &                                                \
            (IMUInt64) IM_IMINT64_CONSTANT (0x00000000ff000000U)) <<  8) |  \
        (((IMUInt64) (val) &                                                \
            (IMUInt64) IM_IMINT64_CONSTANT (0x000000ff00000000U)) >>  8) |  \
        (((IMUInt64) (val) &                                                \
            (IMUInt64) IM_IMINT64_CONSTANT (0x0000ff0000000000U)) >> 24) |  \
        (((IMUInt64) (val) &                                                \
            (IMUInt64) IM_IMINT64_CONSTANT (0x00ff000000000000U)) >> 40) |  \
        (((IMUInt64) (val) &                                                \
            (IMUInt64) IM_IMINT64_CONSTANT (0xff00000000000000U)) >> 56)))

/* Arch specific stuff for speed
 */
#if defined (__GNUC__) && (__GNUC__ >= 2) && defined (__OPTIMIZE__)
#  if defined (__i386__)
#    define IMUINT16_SWAP_LE_BE_IA32(val)                               \
       (__extension__                                                   \
        ({ register IMUInt16 __v, __x = ((IMUInt16) (val));             \
           if (__builtin_constant_p (__x))                              \
             __v = IMUINT16_SWAP_LE_BE_CONSTANT (__x);                  \
           else                                                         \
             __asm__ ("rorw $8, %w0"                                    \
                      : "=r" (__v)                                      \
                      : "0" (__x)                                       \
                      : "cc");                                          \
            __v; }))
#    if !defined (__i486__) && !defined (__i586__)                      \
        && !defined (__pentium__) && !defined (__i686__)                \
        && !defined (__pentiumpro__) && !defined (__pentium4__)
#       define IMUINT32_SWAP_LE_BE_IA32(val)                            \
          (__extension__                                                \
           ({ register IMUInt32 __v, __x = ((IMUInt32) (val));          \
              if (__builtin_constant_p (__x))                           \
                __v = IMUINT32_SWAP_LE_BE_CONSTANT (__x);               \
              else                                                      \
                __asm__ ("rorw $8, %w0\n\t"                             \
                         "rorl $16, %0\n\t"                             \
                         "rorw $8, %w0"                                 \
                         : "=r" (__v)                                   \
                         : "0" (__x)                                    \
                         : "cc");                                       \
              __v; }))
#    else /* 486 and higher has bswap */
#       define IMUINT32_SWAP_LE_BE_IA32(val)                            \
          (__extension__                                                \
           ({ register IMUInt32 __v, __x = ((IMUInt32) (val));          \
              if (__builtin_constant_p (__x))                           \
                __v = IMUINT32_SWAP_LE_BE_CONSTANT (__x);               \
              else                                                      \
                __asm__ ("bswap %0"                                     \
                         : "=r" (__v)                                   \
                         : "0" (__x));                                  \
              __v; }))
#    endif /* processor specific 32-bit stuff */
#    define IMUINT64_SWAP_LE_BE_IA32(val)                               \
       (__extension__                                                   \
        ({ union { IMUInt64 __ll;                                       \
                   IMUInt32 __l[2]; } __w, __r;                         \
           __w.__ll = ((IMUInt64) (val));                               \
           if (__builtin_constant_p (__w.__ll))                         \
             __r.__ll = IMUINT64_SWAP_LE_BE_CONSTANT (__w.__ll);        \
           else                                                         \
             {                                                          \
               __r.__l[0] = IMUINT32_SWAP_LE_BE (__w.__l[1]);           \
               __r.__l[1] = IMUINT32_SWAP_LE_BE (__w.__l[0]);           \
             }                                                          \
           __r.__ll; }))
     /* Possibly just use the constant version and let gcc figure it out? */
#    define IMUINT16_SWAP_LE_BE(val) (IMUINT16_SWAP_LE_BE_IA32 (val))
#    define IMUINT32_SWAP_LE_BE(val) (IMUINT32_SWAP_LE_BE_IA32 (val))
#    define IMUINT64_SWAP_LE_BE(val) (IMUINT64_SWAP_LE_BE_IA32 (val))
#  elif defined (__ia64__)
#    define IMUINT16_SWAP_LE_BE_IA64(val)                               \
       (__extension__                                                   \
        ({ register IMUInt16 __v, __x = ((IMUInt16) (val));             \
           if (__builtin_constant_p (__x))                              \
             __v = IMUINT16_SWAP_LE_BE_CONSTANT (__x);                  \
           else                                                         \
             __asm__ __volatile__ ("shl %0 = %1, 48 ;;"                 \
                                   "mux1 %0 = %0, @rev ;;"              \
                                    : "=r" (__v)                        \
                                    : "r" (__x));                       \
            __v; }))
#    define IMUINT32_SWAP_LE_BE_IA64(val)                               \
       (__extension__                                                   \
         ({ register IMUInt32 __v, __x = ((IMUInt32) (val));            \
            if (__builtin_constant_p (__x))                             \
              __v = IMUINT32_SWAP_LE_BE_CONSTANT (__x);                 \
            else                                                        \
             __asm__ __volatile__ ("shl %0 = %1, 32 ;;"                 \
                                   "mux1 %0 = %0, @rev ;;"              \
                                    : "=r" (__v)                        \
                                    : "r" (__x));                       \
            __v; }))
#    define IMUINT64_SWAP_LE_BE_IA64(val)                               \
       (__extension__                                                   \
        ({ register IMUInt64 __v, __x = ((IMUInt64) (val));             \
           if (__builtin_constant_p (__x))                              \
             __v = IMUINT64_SWAP_LE_BE_CONSTANT (__x);                  \
           else                                                         \
             __asm__ __volatile__ ("mux1 %0 = %1, @rev ;;"              \
                                   : "=r" (__v)                         \
                                   : "r" (__x));                        \
           __v; }))
#    define IMUINT16_SWAP_LE_BE(val) (IMUINT16_SWAP_LE_BE_IA64 (val))
#    define IMUINT32_SWAP_LE_BE(val) (IMUINT32_SWAP_LE_BE_IA64 (val))
#    define IMUINT64_SWAP_LE_BE(val) (IMUINT64_SWAP_LE_BE_IA64 (val))
#  elif defined (__x86_64__)
#    define IMUINT32_SWAP_LE_BE_X86_64(val)                             \
       (__extension__                                                   \
         ({ register IMUInt32 __v, __x = ((IMUInt32) (val));            \
            if (__builtin_constant_p (__x))                             \
              __v = IMUINT32_SWAP_LE_BE_CONSTANT (__x);                 \
            else                                                        \
             __asm__ ("bswapl %0"                                       \
                      : "=r" (__v)                                      \
                      : "0" (__x));                                     \
            __v; }))
#    define IMUINT64_SWAP_LE_BE_X86_64(val)                             \
       (__extension__                                                   \
        ({ register IMUInt64 __v, __x = ((IMUInt64) (val));             \
           if (__builtin_constant_p (__x))                              \
             __v = IMUINT64_SWAP_LE_BE_CONSTANT (__x);                  \
           else                                                         \
             __asm__ ("bswapq %0"                                       \
                      : "=r" (__v)                                      \
                      : "0" (__x));                                     \
           __v; }))
     /* gcc seems to figure out optimal code for this on its own */
#    define IMUINT16_SWAP_LE_BE(val) (IMUINT16_SWAP_LE_BE_CONSTANT (val))
#    define IMUINT32_SWAP_LE_BE(val) (IMUINT32_SWAP_LE_BE_X86_64 (val))
#    define IMUINT64_SWAP_LE_BE(val) (IMUINT64_SWAP_LE_BE_X86_64 (val))
#  else /* generic gcc */
#    define IMUINT16_SWAP_LE_BE(val) (IMUINT16_SWAP_LE_BE_CONSTANT (val))
#    define IMUINT32_SWAP_LE_BE(val) (IMUINT32_SWAP_LE_BE_CONSTANT (val))
#    define IMUINT64_SWAP_LE_BE(val) (IMUINT64_SWAP_LE_BE_CONSTANT (val))
#  endif
#else /* generic */
#  define IMUINT16_SWAP_LE_BE(val) (IMUINT16_SWAP_LE_BE_CONSTANT (val))
#  define IMUINT32_SWAP_LE_BE(val) (IMUINT32_SWAP_LE_BE_CONSTANT (val))
#  define IMUINT64_SWAP_LE_BE(val) (IMUINT64_SWAP_LE_BE_CONSTANT (val))
#endif /* generic */

/* The IM*_TO_?E() macros are defined in imbus_config.h.
 * The transformation is symmetric, so the FROM just maps to the TO.
 */
#define IMINT16_FROM_LE(val)            (IMINT16_TO_LE (val))
#define IMUINT16_FROM_LE(val)           (IMUINT16_TO_LE (val))
#define IMINT16_FROM_BE(val)            (IMINT16_TO_BE (val))
#define IMUINT16_FROM_BE(val)           (IMUINT16_TO_BE (val))
#define IMINT32_FROM_LE(val)            (IMINT32_TO_LE (val))
#define IMUINT32_FROM_LE(val)           (IMUINT32_TO_LE (val))
#define IMINT32_FROM_BE(val)            (IMINT32_TO_BE (val))
#define IMUINT32_FROM_BE(val)           (IMUINT32_TO_BE (val))

#define IMINT64_FROM_LE(val)            (IMINT64_TO_LE (val))
#define IMUINT64_FROM_LE(val)           (IMUINT64_TO_LE (val))
#define IMINT64_FROM_BE(val)            (IMINT64_TO_BE (val))
#define IMUINT64_FROM_BE(val)           (IMUINT64_TO_BE (val))

IM_END_DECLS

#endif
/** @} */
/*
vi:ts=4:nowrap:ai:expandtab
*/
